package com.dabai.iot.core.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.dabai.common.core.exception.ServiceException;
import com.dabai.common.core.utils.Func;
import com.dabai.common.core.utils.StringUtils;
import com.dabai.common.redis.utils.RedisUtils;
import com.dabai.common.tenant.helper.TenantHelper;
import com.dabai.iot.common.constant.IotKeyConstant;
import com.dabai.iot.common.enums.FunctionType;
import com.dabai.iot.common.enums.SecurityType;
import com.dabai.iot.core.cache.CacheKey;
import com.dabai.iot.core.convert.ProductConvert;
import com.dabai.iot.core.dto.ProductQuery;
import com.dabai.iot.core.entity.*;
import com.dabai.iot.core.mapper.*;
import com.dabai.iot.core.service.IDeviceLogService;
import com.dabai.iot.core.service.IProductService;
import com.dabai.iot.core.tsl.*;
import com.dabai.iot.core.tsl.specs.TslDataSpecHelper;
import com.dabai.iot.core.utils.TslUtil;
import com.dabai.iot.core.vo.ProductSelectVO;
import com.dabai.iot.core.vo.ProductVO;
import com.dabai.iot.mqtt.topics.SysTopics;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 产品 Service 实现类
 *
 * @author kev1n
 */
@Service
@RequiredArgsConstructor
public class ProductServiceImpl extends ServiceImpl<ProductMapper, ProductEntity> implements IProductService {

    private final DeviceMapper deviceMapper;
    private final ThingModelBlockMapper thingModelBlockMapper;
    private final ThingModelItemMapper thingModelItemMapper;
    private final ServerSubscribeMapper serverSubscribeMapper;
    private final IDeviceLogService deviceLogService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addProduct(ProductEntity product) {
        if (checkProductNameDuplicate(product)) {
            throw new ServiceException("已经存在相同的产品名称");
        }

        // 生成产品PK，产品PK全系统下唯一
        List<ProductEntity> productList = TenantHelper.ignore(() -> this.list());
        List<String> productKeys = productList.stream().map(ProductEntity::getProductKey).toList();
        String productKey = generateProductKey(productKeys);
        product.setProductKey(productKey);

        product.setTsl(TslUtil.resolveDefaultTsl(productKey));
        // 一型一密 (一机一密时本系统不需要设置产品密钥)
        if (SecurityType.one.getValue().equals(product.getSecurityType())) {
            product.setProductSecret(RandomUtil.randomString(12));
        }

        boolean result = save(product);
        if (result) {
            result = SqlHelper.retBool(thingModelBlockMapper.insert(resolveDefaultThingModel(product)));
            if (!result) {
                // 创建产品默认物模型功能模块失败
                throw new ServiceException("创建产品默认物模型功能模块失败");
            }

            // 创建日志超级表
            deviceLogService.createDeviceLogStb(product.getTenantId(), product.getProductKey());
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateProduct(ProductEntity product) {
        if (checkProductNameDuplicate(product)) {
            throw new ServiceException("已经存在相同的产品名称");
        }

        boolean result = updateById(product);
        if (result) {
            // 删除缓存
            deleteCache(product.getId(), product.getProductKey());
        }

        return result;
    }

    /**
     * 校验产品名称是否重复(企业内唯一)
     */
    boolean checkProductNameDuplicate(ProductEntity product) {
        return this.lambdaQuery()
            .eq(ProductEntity::getProductName, product.getProductName())
            .ne(ObjectUtil.isNotNull(product.getId()), ProductEntity::getId, product.getId())
            .exists();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeProduct(Long productId) {
        // 判断设备
        Long devicesCount = deviceMapper.selectCount(Wrappers.<DeviceEntity>query().lambda().eq(DeviceEntity::getProductId, productId));
        if (devicesCount > 0) {
            throw new ServiceException(StringUtils.format("该产品下还有({})个设备，无法删除", devicesCount));
        }

        // 判断订阅
        Long subscribeCount = serverSubscribeMapper.selectCount(Wrappers.<ServerSubscribeEntity>query().lambda().eq(ServerSubscribeEntity::getProductId, productId));
        if (subscribeCount > 0) {
            throw new ServiceException("该产品还有关联订阅，无法删除");
        }

        ProductEntity product = getById(productId);
        if (product == null) {
            throw new ServiceException("产品不存在");
        }

        boolean result = removeById(productId);
        if (result) {
            // 删除缓存
            deleteCache(product.getId(), product.getProductKey());
        }

        return result;
    }

    @Override
    public ProductVO detailById(@NonNull Long productId) {
        ProductEntity product = getById(productId);
        if (product == null) {
            // 产品不存在
            throw new ServiceException("产品不存在");
        }
        ProductVO vo = ProductConvert.INSTANCE.convertVO(product);

        // 查询产品对应的服务端订阅
        ServerSubscribeEntity serverSubscribe = serverSubscribeMapper.selectOne(Wrappers.<ServerSubscribeEntity>query().lambda().eq(ServerSubscribeEntity::getProductId, productId));
        vo.setSubscribe(serverSubscribe);
        return vo;
    }

    @Override
    public IPage<ProductEntity> getProductPage(IPage<ProductEntity> page, ProductQuery productQuery) {
        QueryWrapper<ProductEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
            .eq(Func.isNotEmpty(productQuery.getId()), ProductEntity::getId, productQuery.getId())
            .eq(Func.isNotEmpty(productQuery.getTenantId()), ProductEntity::getTenantId, productQuery.getTenantId())
            .like(Func.isNotEmpty(productQuery.getProductName()), ProductEntity::getProductName, productQuery.getProductName())
            .eq(Func.isNotEmpty(productQuery.getProtocol()), ProductEntity::getProtocol, productQuery.getProtocol())
            .eq(Func.isNotEmpty(productQuery.getDataType()), ProductEntity::getDataType, productQuery.getDataType())
            .eq(Func.isNotEmpty(productQuery.getConnectType()), ProductEntity::getConnectType, productQuery.getConnectType())
            .orderByDesc(ProductEntity::getCreateTime);

        return page(page, queryWrapper);
    }

    @Override
    public List<ProductSelectVO> select(String productName) {
        List<ProductEntity> list = this.lambdaQuery()
            .select(ProductEntity::getId, ProductEntity::getProductName, ProductEntity::getProductKey)
            .like(Func.isNotEmpty(productName), ProductEntity::getProductName, productName)
            .orderByDesc(ProductEntity::getCreateTime)
            .list();

        return list.stream().map(ProductConvert.INSTANCE::convertSelectVO).collect(Collectors.toList());
    }

    @Override
    public boolean updateProductTsl(Long productId) {
        ProductEntity product = getById(productId);
        if (product == null) {
            throw new ServiceException("产品不存在");
        }

        TslBean tslBean = new TslBean();
        tslBean.setVersion(TslConstant.TSL_VERSION);
        tslBean.setProductKey(product.getProductKey());

        List<ThingModelBlockEntity> thingModelList = thingModelBlockMapper.selectList(
            Wrappers.<ThingModelBlockEntity>query().lambda()
                .eq(ThingModelBlockEntity::getProductId, productId)
        );

        for (ThingModelBlockEntity modelBlock : thingModelList) {
            TslBlock tslBlock = new TslBlock();
            tslBlock.setBlockIdentifier(modelBlock.getBlockIdentifier());
            tslBlock.setBlockName(modelBlock.getBlockName());

            List<ThingModelItemEntity> thingModelItemList = thingModelItemMapper.selectList(
                Wrappers.<ThingModelItemEntity>query().lambda()
                    .eq(ThingModelItemEntity::getBlockId, modelBlock.getId())
                    .orderByAsc(ThingModelItemEntity::getSort)
            );
            for (ThingModelItemEntity thingModelItem : thingModelItemList) {
                if (FunctionType.property.getValue().equals(thingModelItem.getFunctionType())) {

                    TslProperty property = new TslProperty();
                    property.setName(thingModelItem.getName());
                    property.setIdentifier(thingModelItem.getIdentifier());
                    property.setDataType(thingModelItem.getDataType());
                    property.setMethod(thingModelItem.getMethod());
                    property.setDataSpecs(TslDataSpecHelper.createDataSpecs(thingModelItem));

                    tslBlock.getProperties().add(property);
                } else if (FunctionType.command.getValue().equals(thingModelItem.getFunctionType())) {

                    TslCommand command = new TslCommand();
                    command.setName(thingModelItem.getName());
                    command.setIdentifier(thingModelItem.getIdentifier());
                    command.setDataType(thingModelItem.getDataType());
                    command.setDataSpecs(TslDataSpecHelper.createDataSpecs(thingModelItem));

                    tslBlock.getCommands().add(command);
                }
            }

            tslBean.getBlocks().add(tslBlock);
        }

        String tsl = JSON.toJSONString(tslBean);
        product.setTsl(tsl);
        boolean result = updateById(product);
        if (result) {
            // 删除缓存
            deleteCache(product.getId(), product.getProductKey());
        }
        return result;
    }

    @Override
    public ProductEntity getProductCacheById(@NonNull Long productId) {
        String cacheKey = CacheKey.getProductIdKey(productId);
        ProductEntity productCache = TenantHelper.ignore(() -> RedisUtils.getCacheObject(cacheKey));
        if (productCache != null) {
            return productCache;
        }

        final ProductEntity product = getById(productId);
        if (product != null) {
            productCache = product;
            TenantHelper.ignore(() -> RedisUtils.setCacheObject(cacheKey, product));
        }

        return productCache;
    }

    @Override
    public ProductEntity getProductCacheByPk(@NonNull String productKey) {
        String cacheKey = CacheKey.getProductPkKey(productKey);
        ProductEntity productCache = TenantHelper.ignore(() -> RedisUtils.getCacheObject(cacheKey));
        if (productCache != null) {
            return productCache;
        }

        final ProductEntity product = lambdaQuery().eq(ProductEntity::getProductKey, productKey).one();
        if (product != null) {
            productCache = product;
            TenantHelper.ignore(() -> RedisUtils.setCacheObject(cacheKey, product));
        }

        return productCache;
    }

    @Override
    public Map<String, String> getTSLExample(String productKey, String deviceSn) {
        JSONArray blockArray = new JSONArray();

        // 获取所有物模型功能模块
        List<ThingModelBlockEntity> blockList = thingModelBlockMapper.selectList(
            Wrappers.<ThingModelBlockEntity>query().lambda()
                .select(ThingModelBlockEntity::getId, ThingModelBlockEntity::getBlockIdentifier)
                .eq(ThingModelBlockEntity::getProductKey, productKey)
        );

        for (ThingModelBlockEntity block : blockList) {
            // 获取模块标识符
            String blockIdentifier = block.getBlockIdentifier();

            JSONObject blockObject = new JSONObject();
            blockObject.put(IotKeyConstant.BLOCK_ID, blockIdentifier);

            JSONObject propertiesObject = new JSONObject();

            // 获取该模块下的所有属性
            List<ThingModelItemEntity> modelItemList = thingModelItemMapper.selectList(
                Wrappers.<ThingModelItemEntity>query().lambda()
                    .eq(ThingModelItemEntity::getBlockId, block.getId())
            );

            for (ThingModelItemEntity item : modelItemList) {
                String identifier = item.getIdentifier();
                propertiesObject.put(identifier, "上报的值");
            }
            blockObject.put(IotKeyConstant.PROPERTIES, propertiesObject);
            blockArray.add(blockObject);
        }

        JSONObject result = new JSONObject();
        result.put(IotKeyConstant.BLOCKS, blockArray);

        String thingModelTopic = SysTopics.PROPERTY_POST
            .replace("${productKey}", productKey)
            .replace("${deviceSn}", deviceSn);

        HashMap<String, String> map = new HashMap<>();
        map.put("topic", thingModelTopic);
        map.put("message", result.toJSONString());
        return map;
    }

    /**
     * 删除缓存
     */
    private void deleteCache(@NonNull Long id, @NonNull String productKey) {
        TenantHelper.ignore(() -> {
            RedisUtils.deleteObject(CacheKey.getProductIdKey(id));
            RedisUtils.deleteObject(CacheKey.getProductPkKey(productKey));
        });
    }

    /**
     * 默认物模型功能模块
     */
    private ThingModelBlockEntity resolveDefaultThingModel(ProductEntity product) {
        ThingModelBlockEntity block = new ThingModelBlockEntity();
        block.setTenantId(product.getTenantId());
        block.setProductId(product.getId());
        block.setProductKey(product.getProductKey());
        block.setBlockIdentifier(TslConstant.DEFAULT_BLOCK_ID);
        block.setBlockName(TslConstant.DEFAULT_BLOCK_NAME);
        block.setCreateBy(product.getCreateBy());
        block.setCreateDept(product.getCreateDept());
        block.setCreateTime(product.getCreateTime());
        block.setUpdateBy(product.getUpdateBy());
        block.setUpdateTime(product.getUpdateTime());
        block.setDelFlag("0");

        return block;
    }

    /**
     * 生成 ProductKey
     */
    private String generateProductKey(List<String> keys) {
        // 随机生成11位
        String key = RandomUtil.randomString(10);
        if (keys.contains(key)) {
            return generateProductKey(keys);
        }
        return key;
    }

}
